Js模块规范

2019.5.31 星期五

实践

下面一堆东西都作废。
jQuery3.4的封装方式可以到处使用。和 UMD 的语法糖稍微有一点差别。$PS: 咱也不敢问

CMD是中文开发者(玉伯?),可以直接去sea.js 了解到 js 模块的历史。
AMD是英文。中文教程也有一堆

一堆东西,就不赘述了
5分钟上手,原因/特性等也是明明白白。实现没有去了解

Sea.js: https://seajs.github.io/seajs/
https://github.com/seajs/seajs

require.js:https://github.com/requirejs/requirejs
https://requirejs.org/

browserify: https://github.com/browserify/browserify
http://browserify.org/

$PS: seajs, requirejs 都有打包方案。browserify的打包稍微有一些区别(把node包打包浏览器可以用),合并/压缩文件等没有细看;

# 前端模块化开发那点历史

写在前面
不谈什么:传统的模块化开发方式,比如文件拆分、全局变量、命名空间,以及 YUI3 式的模块化开发方式。有兴趣的可阅读:#547
谈什么: 关于 CommonJS、AMD、Node.js、CMD 等相关的故事与未来趋势,很有意思。
不一定精准:本文是基于史实的扯淡,因此部分文字特别是时间都是模糊记忆,不一定精准。关于流派、趋势则是个人在社区的感受,不代表客观看法。(看法都是主观> 的,呵呵)

# Sea.js 使用文档
## 探讨
前端模块化开发那点历史
从 CommonJS 到 Sea.js
与 Node.js 兼容
与 RequireJS 的异同
与 OzJS 的探讨

ES6 模块

# Module 的语法

概述

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

除了静态加载带来的各种好处,ES6 模块还有以下好处。

$PS: 静态加载(非运行加载),所以又很多限制。但是有 import() 函数了

# Module 的加载实现

浏览器加载

传统方法

defer与async的区别是:
defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;
async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
一句话,defer是“渲染完再执行”,async是“下载完就执行”。
另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

加载规则

浏览器加载 ES6 模块,也使用<script>标签,但是要加入type=”module”属性。

浏览器对于带有type=”module”的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。

如果网页有多个<script type="module">,它们会按照在页面出现的顺序依次执行。 $PS: async并不会顺序执行
<script>标签的async属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,再恢复渲染。
一旦使用了async属性,<script type="module">就不会按照在页面出现的顺序执行,而是只要该模块加载完成,就执行该模块。

ES6 模块与 CommonJS 模块的差异

它们有两个重大差异。

(1) CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
(2) CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

第二个差异是因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。
而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

下面重点解释第一个差异。

CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。请看下面这个模块文件lib.js的例子。

Node 加载

概述
Node 对 ES6 模块的处理比较麻烦,因为它有自己的 CommonJS 模块格式,与 ES6 模块格式是不兼容的。
目前的解决方案是,将两者分开,ES6 模块和 CommonJS 采用各自的加载方案。

………

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<p style="text-align:right">2018.6.22 星期五</p>


\# [Node中没搞明白require和import,你会被坑的很惨](http://imweb.io/topic/582293894067ce9726778be9)
ES6标准发布后,module成为标准,标准的使用是以export指令导出接口,以import引入模块,但是在我们一贯的node模块中,我们采用的是CommonJS规范,使用require引入模块,使用module.exports导出接口�??

不把require和import整清楚,会在未来的标准编程中死的很难看�?

## require时代的模�??
node编程中最重要的思想之一就是模块,而正是这个思想,让JavaScript的大规模工程成为可能。模块化编程在js界流行,也是基于此,随后在浏览器端,requirejs和seajs之类的工具包也出现了,可以说在对应规范下,require统治了ES6之前的所有模块化编程,即使现在,在ES6 module被完全实现之前,还是这样�??

node的module遵循CommonJS规范,requirejs遵循AMD,seajs遵循CMD,虽各有不同,但总之还是希望保持较为统一的代码风格�?
```javascript
// a.js
// -------- node -----------
module.exports = {
a : function() {},
b : 'xxx'
};

// ----------- AMD or CMD ----------------
define(function(require, exports, module){
module.exports = {
a : function() {},
b : 'xxx'
};
});
```
可以看出,为了保持风格的高度统一,除了在浏览器端的模块中要使用一个define函数来提供模块的闭包以外,其他代码可以完全一致�?
```
// b.js

// ------------ node ---------
var m = require('./a');
m.a();

// ------------ AMD or CMD -------------
define(function(require, exports, module){
var m = require('./a');
m.a();
});
```
在使用上,也非常相似。虽然AMD or CMD提供了更加丰富的风格,但是我们本文主要是讨论node环境下,所以不做扩展�?

\## ES6中的module
## 该用require还是import�??
require的使用非常简单,它相当于module.exports的传送门,module.exports后面的内容是什么,require的结果就是什么,对象、数字、字符串、函数……再把require的结果赋值给某个变量,相当于把require和module.exports进行平行空间的位置重叠�?

而且require理论上可以运用在代码的任何地方,甚至不需要赋值给某个变量之后再使用,比如�??
```javascript
require('./a')(); // a模块是一个函数,立即执行a模块函数
var data = require('./a').data; // a模块导出的是一个对�??
var a = require('./a')[0]; // a模块导出的是一个数�??
```
你在使用时,完全可以忽略模块化这个概念来使用require,仅仅把它当做一个node内置的全局函数,它的参数甚至可以是表达式:
```javascript
require(process.cwd() + '/a');
```

但是import则不同,它是编译时的(require是运行时的),它必须放在文件开头,而且使用格式也是确定的,不容置疑。它不会将整个模块运行后赋值给某个变量,而是只选择import的接口进行编译,这样在性能上比require好很多�?
从理解上,require是赋值过程,import是解构过程,当然,require也可以将结果解构赋值给一组变量,但是import在遇到default时,和require则完全不同:var $ = require('jquery');和import $ from 'jquery'是完全不同的两种概念�??

上面完全没有回答“改用require还是import?”这个问题,因为这个问题就目前而言,根本没法回答,因为目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require。这也是为什么在模块导出时使用module.exports,在引入模块时使用import仍然起效,因为本质上,import会被转码为require去执行�?

但是,我们要知道这样一个道理,ES7很快也会发布,js引擎们会尽快实现ES6标准的规定,如果一个引擎连标准都实现不了,就会被淘汰,ES6是迟早的事。如果你现在仍然在代码中部署require,那么等到ES6被引擎支持时,你必须升级你的代码,而如果现在开始部署import,那么未来可能只需要做很少的改动。(完)


\# [JS模块规范:AMD、UMD、CMD、commonJS、ES6 module]
## commonJS
1、模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存�?
2、模块加载会阻塞接下来代码的执行,需要等到模块加载完成才能继续执行——同步加载�?

环境:服务器环境
应用:nodejs的模块规范是参照commonJS实现的�?

1、导入:require('路径')
2、导出:module.exports和exports
注意:module.exports和exports的的区别是exports只是对module.exports的一个引用,相当于Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行var exports = module.exports;这样的命令�?
demo

## AMD
1、异步加�??
2、管理模块之间的依赖性,便于代码的编写和维护�??

环境:浏览器环境
应用:requireJS是参照AMD规范实现�??

1、导入:require(['模块名称'], function ('模块变量引用'){// 代码});
3、导出:define(function (){return '�??');

## CMD
1、CMD是在AMD基础上改进的一种规范,和AMD不同在于对依赖模块的执行时机处理不同,CMD是就近依赖,而AMD是前置依赖�?

环境:浏览器环境
应用:seajs是参照UMD规范实现的,requireJS的最新的几个版本也是部分参照了UMD规范的实�??

1、导入:define(function(require, exports, module) {});
2、导出:define(function (){return '�??');


## UMD
1、兼容AMD和commonJS规范的同时,还兼容全局引用的方�??

环境:浏览器或服务器环境
应用:无

1、无导入导出规范,只有如下的一个常规写法:

## ES6 module
1、按需加载(编译时加载�??
2、import和export命令只能在模块的顶层,不能在代码块之中(如:if语句中),import()语句可以在代码块中实现异步动态按需动态加�??

环境:浏览器或服务器环境(以后可能支持)
应用:ES6的最新语法支持规�??

1、导入:import {模块名A,模块名B...} from '模块路径'
2、导出:export和export default
3、import('模块路径').then()方法
注意:export只支持对象形式导出,不支持值的导出,export default命令用于指定模块的默认输出,只支持值导出,但是只能指定一个,本质上它就是输出一个叫做default的变量或方法�??



\# [关于AMD,CMD,CommonJS及UMD规范](https://www.tuicool.com/articles/nueqi27)
## AMD规范
AMD规范,全称”Asynchronous Module Definition”,称为 异步模块加载规范 。
一般应用在浏览器端。
流行的浏览器端异步加载库 RequireJS ( 中文网站 )实现的就是AMD规范。

AMD讲究的是前置执行。
```javascript
// filename: foo.js
define(['jquery', 'underscore'], function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned

// exposed public methods
return {
b: b,
c: c
}
});
```
define 是AMD规范用来声明模块的接口,
示例中的第一个参数是一个数组,表示当前模块的依赖。第二个参数是一个回调函数,表示此模块的执行体。
只有当依赖数组中的所有依赖模块都是可用的时,AMD模块加载器(比如RequireJS)才会去执行回调函数并返回此模块的暴露接口。


## CMD规范
CMD规范,全称”Common Module Definition”,称为 通用模块加载规范 。$PS: 普通模块加载规范
一般也是用在浏览器端。
浏览器端异步加载库 Sea.js 实现的就是CMD规范。
```javascript
define(function (require, exports, module) {
// load dependence
var $ = require('jquery');

// methods
function myFunc(){};

// exposed public methods
return myFunc;
})
```

CMD规范倾向依赖就近 ,稍微复杂一点例子
```javascript
define(function (requie, exports, module) {
// 依赖可以就近书写
var a = require('./a');
a.test();

// ...
// 软依赖
if (status) {
var b = requie('./b');
b.test();
}
});
```

## CommonJS规范
根据CommonJS规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的。

CommonJS规范一般应用于服务端(Node.js平台),而且CommonJS加载模块采用的是同步方式(这跟他适用的场景有关系)。

同时,得力于 Browserify 这样的第三方工具,我们可以在浏览器端使用采用CommonJS规范的js文件。
```javascript
// filename: foo.js
var $ = require('jquery');
var _ = require('underscore');

// methods
function a(){}; // private because it's omitted from module.exports (see below)
function b(){}; // public because it's defined in module.exports
function c(){}; // public because it's defined in module.exports

// exposed public methods
module.exports = {
b: b,
c: c
};
```

## UMD规范
因为AMD,CommonJS规范是两种不一致的规范,虽然他们应用的场景也不太一致,但是人们仍然是期望有一种统一的规范来支持这两种规范。于是,UMD(Universal Module Definition,称之为 通用模块规范 )规范诞生了。

客观来说,这个UMD规范看起来的确没有AMD和CommonJS规范简约。但是它支持AMD和CommonJS规范,同时还支持古老的全局模块模式。
```javascript
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory); // $_PS: factory 依赖jquery
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
// methods
function myFunc(){};

// exposed public method
return myFunc;
}));

// $PS: 上面是有依赖的(jquery)。如果没有依赖应该是下文
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define('myFunc', factory);
// define('myFunc',[], factory); // 中间的依赖可以省略
// define(["alpha"], function (alpha) {}) // 匿名模块。 define还可以只有一个参数define({})

} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.myFunc = factory(root);
}
}(typeof window !== "undefined" ? window : this, function (root) {
// methods
function myFunc(){};

// exposed public method
return myFunc;
}));
```

个人觉得UMD规范更像一个语法糖。应用UMD规范的js文件其实就是一个立即执行函数。函数有两个参数,第一个参数是当前运行时环境,第二个参数是模块的定义体。在执行UMD规范时,会优先判断是当前环境是否支持AMD环境,然后再检验是否支持CommonJS环境,否则认为当前环境为浏览器环境( window )。当然具体的判断顺序其实是可以调换的。


\# [什么是前端模块规范AMD,CMD,CommonJS和UMD?](https://www.jianshu.com/p/00ee4e45c0cd)
这是webpack下babel的配置文�??.babelrc�??
```json
{
"presets":[
{
"es2015":{
"modules":false
}
}
],
"plugins":[]
}
```
使ES6模块语法转换到另一个模块类�??(默认启用“commonjs”)�?? 设置为假则不变换模块。或者传�??(“amd”、“umd”,“systemjs”、“commonjs”).




其它:
[关于 CommonJS AMD CMD UMD 规范的差异总结](http://www.cnblogs.com/imwtr/p/4666181.html)
[编码规范:模块和加载器](http://codespec.lookapi.org/#/module?id=moduleid)

~~~~~~~~

jQuery的封装方式

2019.5.30 星期四

封装pagination。借鉴了jquery-3.4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// cc from jquery: <https://code.jquery.com/jquery-3.4.1.js>
;( function( global, factory ) {

"use strict";

if ( typeof module === "object" && typeof module.exports === "object" ) {

// For CommonJS and CommonJS-like environments where a proper `window`
// is present, execute the factory and get jQuery.
// For environments that do not have a `window` with a `document`
// (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info.

module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "CoPagination requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}

// Pass this if window is not defined yet
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {


/**
* @param {string} param0.hideOnSinglePage - 只有一页时是否隐藏. $_NEXT
* @param {string} param0.pagerCount - 显示页码按钮的数量,当总页数超过该值时会折叠。大于等�?? 5 且小于等�?? 21 的奇�?? $PS: 不知道为啥小�??21
* @param {Array} param0.layout - 要展示的内容。limits, prev, pager, next, jumper, total,count
*/
function CoPagination({ele='.co-pagination',hideOnSinglePage=false,curr=1,count=1,limit=10,limits=[5,10,15,20],pagerCount=7,prevText='<',nextText='>',layout=['limits','prev','pager','next','jumper']}={}){
this.dom=document.querySelectorAll(ele)[0]
this.curr=curr

pagerCount%2==0&&(pagerCount+=1)
pagerCount<7&&(pagerCount=7)

this.count=count
this.limit=limit
this.limits=limits
this.pagerCount=pagerCount
this.prevText=prevText
this.nextText=nextText
this.layout=layout

this.init()
}
CoPagination.prototype={
constructor:CoPagination,
init:function(){
this.renderHtml()
},
renderHtml(){},
renderPagerHtml(curr){},
changeCurr(curr){},
changeLimit(limit){},

}


// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.

// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

// $PS: 和上面定义的 UMD 语法糖 稍微有些区别
if ( typeof define === "function" && define.amd ) {
define( "copagination", [], function() {
return CoPagination;
} );
}



// Expose jQuery and $ identifiers, even in AMD
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( !noGlobal ) {
// window.jQuery = window.$ = jQuery;
window.CoPagination = CoPagination;
}



return CoPagination;
});


/* ES6
class CoPagination{
constructor({dom=''}={}){
this.dom=dom
}
_pagerHtml=''
init(){}
}
*/

knowledge is no pay,reward is kindness
0%